#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

// 错误码定义
#define EMBED_SUCCESS 0
#define EMBED_FILE_OPEN_ERROR -1
#define EMBED_FILE_READ_ERROR -2
#define EMBED_RESOURCE_UPDATE_ERROR -3
#define EMBED_INVALID_PARAMETER -4

/**
 * @brief 将清单文件嵌入到可执行文件中
 * 
 * @param executablePath 目标可执行文件路径
 * @param manifestPath   清单文件路径
 * @param resourceId     资源ID (应用程序通常用1，DLL用2)
 * @return int           操作结果状态码
 */
int EmbedManifest(const char* executablePath, 
                 const char* manifestPath, 
                 int resourceId) {
    // 参数验证
    if (!executablePath || !manifestPath || resourceId <= 0) {
        return EMBED_INVALID_PARAMETER;
    }

    // 读取清单文件内容
    FILE* manifestFile = fopen(manifestPath, "rb");
    if (!manifestFile) {
        return EMBED_FILE_OPEN_ERROR;
    }

    fseek(manifestFile, 0, SEEK_END);
    long manifestSize = ftell(manifestFile);
    fseek(manifestFile, 0, SEEK_SET);

    BYTE* manifestData = (BYTE*)malloc(manifestSize);
    if (!manifestData) {
        fclose(manifestFile);
        return EMBED_FILE_READ_ERROR;
    }

    if (fread(manifestData, 1, manifestSize, manifestFile) != manifestSize) {
        free(manifestData);
        fclose(manifestFile);
        return EMBED_FILE_READ_ERROR;
    }
    fclose(manifestFile);

    // 打开目标可执行文件进行资源更新
    HANDLE hUpdate = BeginUpdateResourceA(executablePath, FALSE);
    if (!hUpdate) {
        free(manifestData);
        return GetLastError() == ERROR_FILE_NOT_FOUND ? 
               EMBED_FILE_OPEN_ERROR : EMBED_RESOURCE_UPDATE_ERROR;
    }

    // 更新清单资源
    BOOL result = UpdateResourceA(
        hUpdate,                   // 更新句柄
        RT_MANIFEST,               // 资源类型: 清单
        MAKEINTRESOURCEA(resourceId), // 资源ID
        MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), // 语言: 中性
        manifestData,              // 清单数据
        manifestSize               // 数据大小
    );

    free(manifestData);
    
    if (!result) {
        EndUpdateResourceA(hUpdate, TRUE); // 放弃更改
        return EMBED_RESOURCE_UPDATE_ERROR;
    }

    // 提交更改
    if (!EndUpdateResourceA(hUpdate, FALSE)) {
        return EMBED_RESOURCE_UPDATE_ERROR;
    }

    return EMBED_SUCCESS;
}


int main(int argc, char* argv[]) {
    // 设置控制台编码为UTF-8
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(CP_UTF8);

    printf("清单嵌入工具 v1.0\n");
    printf("用法: %s <可执行文件> <清单文件> [资源ID]\n", argv[0]);
    printf("示例: %s myapp.exe manifest.xml 1\n\n", argv[0]);
    
    // 检查参数数量
    if (argc < 3) {
        fprintf(stderr, "错误: 参数不足\n");
        return EMBED_INVALID_PARAMETER;
    }
    
    const char* executable = argv[1];
    const char* manifest = argv[2];
    int resourceId = 1;  // 默认资源ID
    
    // 如果提供了资源ID参数
    if (argc >= 4) {
        resourceId = atoi(argv[3]);
        if (resourceId <= 0) {
            fprintf(stderr, "错误: 无效的资源ID '%s' (必须为正整数)\n", argv[3]);
            return EMBED_INVALID_PARAMETER;
        }
    }
    
    printf("正在嵌入清单...\n");
    printf("目标文件: %s\n", executable);
    printf("清单文件: %s\n", manifest);
    printf("资源ID  : %d\n", resourceId);
    
    int result = EmbedManifest(executable, manifest, resourceId);
    
    switch (result) {
        case EMBED_SUCCESS:
            printf("\n操作成功! 清单已嵌入到可执行文件中。\n");
            break;
        case EMBED_FILE_OPEN_ERROR:
            fprintf(stderr, "\n错误: 无法打开文件 (请检查文件路径和权限)\n");
            break;
        case EMBED_FILE_READ_ERROR:
            fprintf(stderr, "\n错误: 读取清单文件失败\n");
            break;
        case EMBED_RESOURCE_UPDATE_ERROR:
            fprintf(stderr, "\n错误: 更新资源失败 (错误码: %d)\n", GetLastError());
            break;
        case EMBED_INVALID_PARAMETER:
            fprintf(stderr, "\n错误: 无效参数\n");
            break;
        default:
            fprintf(stderr, "\n未知错误: %d\n", result);
    }
    
    return result;
}